﻿//=============================================================================================
// Szamitogepes grafika hazi feladat keret. Ervenyes 2012-tol.
// A //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// sorokon beluli reszben celszeru garazdalkodni, mert a tobbit ugyis toroljuk.
// A beadott program csak ebben a fajlban lehet, a fajl 1 byte-os ASCII karaktereket tartalmazhat.
// Tilos:
// - mast "beincludolni", illetve mas konyvtarat hasznalni
// - faljmuveleteket vegezni (printf is fajlmuvelet!)
// - new operatort hivni az onInitialization f?ggv?nyt kiv?ve, a lefoglalt adat korrekt felszabad?t?sa n?lk?l
// - felesleges programsorokat a beadott programban hagyni
// - tovabbi kommenteket a beadott programba irni a forrasmegjelolest kommentjeit kiveve
// ---------------------------------------------------------------------------------------------
// A feladatot ANSI C++ nyelvu forditoprogrammal ellenorizzuk, a Visual Studio-hoz kepesti elteresekrol
// es a leggyakoribb hibakrol (pl. ideiglenes objektumot nem lehet referencia tipusnak ertekul adni)
// a hazibeado portal ad egy osszefoglalot.
// ---------------------------------------------------------------------------------------------
// A feladatmegoldasokban csak olyan gl/glu/glut fuggvenyek hasznalhatok, amelyek
// 1. Az oran a feladatkiadasig elhangzottak ES (logikai AND muvelet)
// 2. Az alabbi listaban szerepelnek:
// Rendering pass: glBegin, glVertex[2|3]f, glColor3f, glNormal3f, glTexCoord2f, glEnd, glDrawPixels
// Transzformaciok: glViewport, glMatrixMode, glLoadIdentity, glMultMatrixf, gluOrtho2D,
// glTranslatef, glRotatef, glScalef, gluLookAt, gluPerspective, glPushMatrix, glPopMatrix,
// Illuminacio: glMaterialfv, glMaterialfv, glMaterialf, glLightfv
// Texturazas: glGenTextures, glBindTexture, glTexParameteri, glTexImage2D, glTexEnvi,
// Pipeline vezerles: glShadeModel, glEnable/Disable a kovetkezokre:
// GL_LIGHTING, GL_NORMALIZE, GL_DEPTH_TEST, GL_CULL_FACE, GL_TEXTURE_2D, GL_BLEND, GL_LIGHT[0..7]
//
// NYILATKOZAT
// ---------------------------------------------------------------------------------------------
// Nev    : Pinter Laszlo Csaba
// Neptun : OPC88I
// ---------------------------------------------------------------------------------------------
// ezennel kijelentem, hogy a feladatot magam keszitettem, es ha barmilyen segitseget igenybe vettem vagy
// mas szellemi termeket felhasznaltam, akkor a forrast es az atvett reszt kommentekben egyertelmuen jeloltem.
// A forrasmegjeloles kotelme vonatkozik az eloadas foliakat es a targy oktatoi, illetve a
// grafhazi doktor tanacsait kiveve barmilyen csatornan (szoban, irasban, Interneten, stb.) erkezo minden egyeb
// informaciora (keplet, program, algoritmus, stb.). Kijelentem, hogy a forrasmegjelolessel atvett reszeket is ertem,
// azok helyessegere matematikai bizonyitast tudok adni. Tisztaban vagyok azzal, hogy az atvett reszek nem szamitanak
// a sajat kontribucioba, igy a feladat elfogadasarol a tobbi resz mennyisege es minosege alapjan szuletik dontes.
// Tudomasul veszem, hogy a forrasmegjeloles kotelmenek megsertese eseten a hazifeladatra adhato pontokat
// negativ elojellel szamoljak el es ezzel parhuzamosan eljaras is indul velem szemben.
//=============================================================================================

#include <math.h>
#include <stdlib.h>

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
// MsWindows-on ez is kell
#include <windows.h>
#endif // Win32 platform

#include <GL/gl.h>
#include <GL/glu.h>
// A GLUT-ot le kell tolteni: http://www.opengl.org/resources/libraries/glut/
#include <GL/glut.h>
#include <stdio.h>


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Innentol modosithatod...


struct Vector {
   float x, y, z;

   Vector( ) {
    x = y = z = 0;
   }
   Vector(float x0, float y0, float z0 = 0) {
    x = x0; y = y0; z = z0;
   }
   Vector operator*(float a) {
    return Vector(x * a, y * a, z * a);
   }
   Vector operator/(float a) {
       return Vector(x / a, y / a, z / a);
   }
   Vector operator+(const Vector& v) {
     return Vector(x + v.x, y + v.y, z + v.z);
   }
   Vector operator-(const Vector& v) {
     return Vector(x - v.x, y - v.y, z - v.z);
   }
   float operator*(const Vector& v) {
    return (x * v.x + y * v.y + z * v.z);
   }
   Vector operator%(const Vector& v) {
    return Vector(y*v.z-z*v.y, z*v.x - x*v.z, x*v.y - y*v.x);
   }
   float Length() { return sqrt(x * x + y * y + z * z); }

   Vector Normalize() {
       return Vector(x / Length(), y / Length(), z / Length());
   }
};

struct Color {
   float r, g, b;

   Color( ) {
    r = g = b = 0;
   }
   Color(float r0, float g0, float b0) {
    r = r0; g = g0; b = b0;
   }
   Color operator*(float a) {
    return Color(r * a, g * a, b * a);
   }
   Color operator*(const Color& c) {
    return Color(r * c.r, g * c.g, b * c.b);
   }
   Color operator+(const Color& c) {
     return Color(r + c.r, g + c.g, b + c.b);
   }
};


class Quaternion {
    float s;
    Vector d;
public:
    Quaternion( ){
        this->s = 0;
        this->d = Vector(0.0f, 0.0f, 0.0f);
    }
    Quaternion(float s0, float x0, float y0, float z0) {
        this->s = s0;
        this->d = Vector(x0, y0, z0);
    }
    Quaternion(float s0, Vector d0) {
        this->s = s0;
        this->d = d0;
    }

    Quaternion operator*( float f ) {
        return Quaternion( s * f, d * f );
    }

    Quaternion operator%( Quaternion& q ) {
        return Quaternion( s * q.s - d * q.d, q.d * s + d * q.s + d % q.d );
    }

    float GetRotationAngle( ) {
        float cosa2 = s;
        float sina2 = d.Length();
        float angRad = atan2( sina2, cosa2 ) * 2;
        return angRad * 180 / M_PI;
    }

    Vector& GetAxis( ) { return d; }

    float& GetRealPart( ) { return s; }

    float getAbs( ) {
        return sqrt(s * s + d.x * d.x + d.y * d.y + d.z * d.z);
    }

    Quaternion getReciprocal() {
        Quaternion q = Quaternion(this->s, this->d * (-1.0f));
        return q * (1 / getAbs( ) * getAbs( ));
    }
};

const int screenWidth = 600;
const int screenHeight = 600;

int getPseudoRandomSeed() {
    unsigned int pseudoSeed = 2012;
    pseudoSeed = (pseudoSeed * 21 + 1986) % 478;
    return pseudoSeed;
}

class VecContainer {
public:
    VecContainer() :
            num(0) {
    }
    void add(Vector obj) {
        if (num != 100)
            items[num++] = obj;
    }
    Vector operator[](int pos) {
        return items[pos];
    }

    int getCounter() {
        return num;
    }

private:
    int num;
    Vector items[100];
};




class CRSpline {
public:

    Vector cP[100];
    Vector vP[100];
    float tP[100];
    Vector a0, a1, a2, a3;
    VecContainer ctr;

    CRSpline() {

    }

    void init() {
    	for (int i = 0; i < ctr.getCounter(); i++) {
            cP[i] = ctr[i];
        }
        calcParam();
    }

    void calcParam() {
        for (float i = 0; i < ctr.getCounter(); i++) {
            tP[(int) i] = i ;
        }

    }

    void calcSpeed() {

        for (int i = 1; i < ctr.getCounter() - 1; i++) {

            vP[i] = (((cP[i] - cP[i - 1]) / (tP[i] - tP[i - 1])) + ((cP[i + 1] - cP[i]) / (tP[i + 1] - tP[i]))) / 2;

        }
        vP[0] = Vector(-0.05, -0.05);
        vP[ctr.getCounter()] = Vector(-0.05, -0.05);
    }

    Vector getCoord(float t, int i) {
        a0 = cP[i];
        a1 = vP[i];
        a2 = (((cP[i + 1] - cP[i]) * 3.0) / pow((tP[i + 1] - tP[i]), 2)) - ((vP[i + 1] + vP[i] * 2.0) / (tP[i + 1] - tP[i]));
        a3 = ((vP[i + 1] + vP[i]) / pow((tP[i + 1] - tP[i]), 2)) - (((cP[i + 1] - cP[i]) * 2.0) / pow((tP[i + 1] - tP[i]), 3));
        Vector retVec = a3 * pow(t - tP[i], 3) + a2 * pow(t - tP[i], 2) + a1 * (t - tP[i]) + a0;
        return retVec;
    }

    Vector getSpeed(float t, int i) {
        a1 = vP[i];
        a2 = (((cP[i + 1] - cP[i]) * 3.0) / pow((tP[i + 1] - tP[i]), 2)) - ((vP[i + 1] + vP[i] * 2.0) / (tP[i + 1] - tP[i]));
        a3 = ((vP[i + 1] + vP[i]) / pow((tP[i + 1] - tP[i]), 2)) - (((cP[i + 1] - cP[i]) * 2.0) / pow((tP[i + 1] - tP[i]), 3));
        Vector retVec = a3 * 3 * pow(t - tP[i], 2) + a2 * 2 * (t - tP[i]) + a1;
        return retVec;
    }

    Vector * p;
       int 	np; // np-th degree polynomial = np+1 points

       float B(int i, float t) {
       	float choose = 1;
          for(int j = 1; j <= i; j++)
    	    choose *= (float)(np-j+1)/j;
          return choose * pow(t, i) * pow(1-t, np-i);
       }



    void onDraw() {
        calcSpeed();
        np = ctr.getCounter()-1;
        glBegin(GL_LINE_STRIP);
        for(float t = 0;t<1;t+=0.01){
        	Vector coord = Vector(0.0,0.0,0.0);
        	for(int i = 0; i <= np; i++){
        		 coord = coord + ctr[i] * B(i,t);
        	}
        	glVertex3f(coord.x,coord.y,coord.z);
        }
        glEnd();


     /*   glBegin(GL_LINE_STRIP);
        glColor3f(1.0, 0.0, 0.0);

        for (int i = 0; i < ctr.getCounter() - 1; i++) {
            for (float t = tP[i]; t < tP[i + 1]; t += 0.1) {
                Vector coord = getCoord(t, i);
                glVertex3f(coord.x, coord.y,coord.z);
            }
        }
        glEnd();*/
    }

};


class CrContainer {
public:
    CrContainer() :
            num(0) {
    }
    void add(CRSpline obj) {
        if (num != 100)
            items[num++] = obj;
    }
    CRSpline operator[](int pos) {
        return items[pos];
    }

    int getCounter() {
        return num;
    }

private:
    int num;
    CRSpline items[100];
};
class Ellipsoid {
    public:
        Ellipsoid(Vector newCenterPosition, float newRadius1, float newRadius2, float newRadius3) {
            this->centerPosition = newCenterPosition;
            this->radius1 = newRadius1;
            this->radius2 = newRadius2;
            this->radius3 = newRadius3;
        }

        void Draw(char type) {
            float du = M_PI / 150.0f;
            float dv = M_PI / 150.0f;
            glBegin(GL_TRIANGLES);
                for (float u = 0.0f; u < 2.0f * M_PI; u += du)
                    for (float v = 0.0f; v < M_PI; v += dv) {
                        if (type == 'b' && u < M_PI && v < M_PI / 2.5f)
                            continue;
                        if (type == 'g' && u > M_PI && v > M_PI / 2.5f)
                            continue;
                        Vector a = getPoint(u, v);
                        Vector b = getPoint(u + du, v);
                        Vector c = getPoint(u + du, v + dv);
                        Vector d = getPoint(u , v + dv);

                        Vector direction = ((a - b) % (c - b)).Normalize();
                        glNormal3f(direction.x, direction.y, direction.z);
                        glVertex3f(a.x, a.y, a.z);
                        glVertex3f(b.x, b.y, b.z);
                        glVertex3f(c.x, c.y, c.z);

                        direction = ((d - a) % (c - a)).Normalize();
                        glNormal3f(direction.x, direction.y, direction.z);
                        glVertex3f(a.x, a.y, a.z);
                        glVertex3f(c.x, c.y, c.z);
                        glVertex3f(d.x, d.y, d.z);
                    }
            glEnd( );
        }
    private:
        Vector centerPosition;
        float radius1, radius2, radius3;

        Vector getPoint(float u, float v) {
            float x = radius1 * cos(u) * sin(v);
            float y = radius2 * sin(u) * sin(v);
            float z = radius3 * cos(v);

            return Vector(x, y, z);
        }
};

class Cylinder {
    public:

        Cylinder(Vector newCenterPosition, float newHeight, float newRadius) {
            this->centerPosition = newCenterPosition;
            this->height = newHeight;
            this->radius = newRadius;
        }
        void Draw( ) {

            float du = M_PI / 50.0f;

            glBegin(GL_TRIANGLE_FAN);
            glNormal3f(0.0f, 1.0f, 0.0f);
            glVertex3f(centerPosition.x, centerPosition.y + height / 2.0f, centerPosition.z);
            for (float u = 0.0f; u < 2.1f * M_PI; u += du)
                glVertex3f(centerPosition.x + radius * sin(u), centerPosition.y + height / 2.0f, centerPosition.z + radius * cos(u));
            glEnd( );

            glBegin(GL_TRIANGLE_STRIP);
            for (float u = 0.0f; u < 2.1f * M_PI; u += du) {
                glNormal3f(cos(u), 0, sin(u));
                glVertex3f(centerPosition.x + radius * cos(u), centerPosition.y - height / 2.0f, centerPosition.z + radius * sin(u));
                glVertex3f(centerPosition.x + radius * cos(u), centerPosition.y + height / 2.0f, centerPosition.z + radius* sin(u));
            }
            glEnd( );


            glBegin(GL_TRIANGLE_FAN);
            glNormal3f(0.0f, -1.0f, 0.0f);
            glVertex3f(centerPosition.x, centerPosition.y - height / 2.0f, centerPosition.z);
            for (float u = 0.0f; u < 2.1f * M_PI; u += du)
                glVertex3f(centerPosition.x + radius * sin(u), centerPosition.y - height / 2.0f, centerPosition.z + radius * cos(u));
            glEnd( );
        }
    private:
        Vector centerPosition;
        float height;
        float radius;
};

class Cuboid {
    public:
        Cuboid(Vector newCenterPosition, float newLength, float newWidth, float newHeigth) {
            this->centerPosition = newCenterPosition;
            this->length = newLength;
            this->width = newWidth;
            this->heigth = newHeigth;
        }
        void Draw( ) {
            glBegin(GL_TRIANGLE_FAN);
                glNormal3f(0.0f, 1.0f, 0.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z + length / 2.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z + length / 2.0f);
            glEnd( );

            glBegin(GL_TRIANGLE_FAN);
                glNormal3f(0.0f, 0.0f, 1.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z + length / 2.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z + length / 2.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z + length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z + length / 2.0f);

            glBegin(GL_TRIANGLE_FAN);
                glNormal3f(-1.0f, 0.0f, 0.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z + length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z + length / 2.0f);
            glEnd( );

            glBegin(GL_TRIANGLE_FAN);
                glNormal3f(0.0f, 0.0f, -1.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z - length / 2.0f);
            glEnd( );

            glBegin(GL_TRIANGLE_FAN);
                glNormal3f(1.0f, 0.0f, 0.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z + length / 2.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z + length / 2.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y + heigth / 2.0f, centerPosition.z - length / 2.0f);
            glEnd( );

            glBegin(GL_TRIANGLE_FAN);
                glNormal3f(0.0f, -1.0f, 0.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z - length / 2.0f);
                glVertex3f(centerPosition.x - width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z + length / 2.0f);
                glVertex3f(centerPosition.x + width / 2.0f, centerPosition.y - heigth / 2.0f, centerPosition.z + length / 2.0f);
            glEnd( );
        }
    private:
        Vector centerPosition;
        float length, width, heigth;
};

class Cone {
    public:
        Cone(Vector centerPosition, float height, float radius) {
            this->centerPosition = centerPosition;
            this->height = height;
            this->radius = radius;
        }

        void Draw( ) {
            float du = M_PI / 50.0f;

            glBegin(GL_TRIANGLE_FAN);
            glNormal3f(0.0f, -1.0f, 0.0f);
            glVertex3f(centerPosition.x, centerPosition.y - height / 2.0f, centerPosition.z);
            for (float u = 0.0f; u < 2.1f * M_PI; u += du)
                glVertex3f(centerPosition.x + radius * sin(u), centerPosition.y - height / 2.0f, centerPosition.z + radius * cos(u));
            glEnd( );

            glBegin(GL_TRIANGLE_STRIP);
            for (float u = 0.0f; u < 2.1f * M_PI; u += du) {
                glNormal3f(cos(u), 0, sin(u));
                glVertex3f(centerPosition.x + radius * cos(u), centerPosition.y - height / 2.0f,centerPosition.z + radius * sin(u));
                glVertex3f(centerPosition.x, centerPosition.y + height / 2.0f, centerPosition.z);
            }
            glEnd( );
        }
    private:
        Vector centerPosition;
        float height;
        float radius;
};



class Seal{
public:
	VecContainer cp;
	CrContainer crc;
	CRSpline cr[4];
	int size;
	CRSpline c1;
	CRSpline c2;
	CRSpline c3;
	CRSpline c4;
	int np;
	int vBufCount;

	Seal(){}

    float B(int i, float t) {
    	float choose = 1;
       for(int j = 1; j <= i; j++)
 	    choose *= (float)(np-j+1)/j;
       return choose * pow(t, i) * pow(1-t, np-i);
    }


	void init() {
		float kappa = 4*(sqrt(2)-1)/3;
		printf("%f\n",kappa);
		kappa*=3;

		cp.add(Vector(0.5, 1, 0));
		cp.add(Vector(0, 2, 0));
		cp.add(Vector(0, 4, 0));
		cp.add(Vector(0.5, 5, 0));

		cp.add(Vector(2, 0, 0));
		cp.add(Vector(2, 2, 0));
		cp.add(Vector(2, 4, 0));
		cp.add(Vector(2, 6, 0));

		cp.add(Vector(4, 0, 0));
		cp.add(Vector(4, 2, 0));
		cp.add(Vector(4, 4, 0));
		cp.add(Vector(4, 6, 0));

		cp.add(Vector(5.5, 1, 0));
		cp.add(Vector(6, 2, 0));
		cp.add(Vector(6, 4, 0));
		cp.add(Vector(5.5, 5, 0));






		size = sqrt(cp.getCounter());
		np = size-1;

		printf("%d\n",size);
	}

	void init2() {






			size = sqrt(cp.getCounter());
			np = size-1;

			printf("%d\n",size);
		}


	void draw() {
		float step = 0.01;
		for(float u = 0;u<1;u+=step){
			for(float v=0;v<1;v+=step){

				Vector coord1 = Vector(0.0,0.0,0.0);
				Vector coord2 = Vector(0.0,0.0,0.0);
				Vector coord3 = Vector(0.0,0.0,0.0);
				Vector coord4 = Vector(0.0,0.0,0.0);
					for (int i = 0; i <= np; i++) {
						for(int j = 0;j <= np;j++){
							coord1 = coord1 + cp[i*size+j] * B(i, u) * B(j,v);
							coord2 = coord2 + cp[i*size+j] * B(i, u+step) * B(j,v);
							coord3 = coord3 + cp[i*size+j] * B(i, u+step) * B(j,v+step);
							coord4 = coord4 + cp[i*size+j] * B(i, u) * B(j,v+step);
						}


				}
		            float amb[4] = {0.74f, 0.71f, 0.42f, 1.0f};
		            float diff[4] = {0.74f, 0.71f, 0.42f, 1.0f};
		            float spec[4] = {0.5f, 0.5f, 0.5f, 1.0f};

		            glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
		            glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
		            glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
		            glMaterialf(GL_FRONT, GL_SHININESS, 200);
					glBegin(GL_TRIANGLES);
					Vector norm1 = (coord1-coord2) % (coord3-coord2);
					norm1 = norm1.Normalize();
					glNormal3f(norm1.x,norm1.y,norm1.z);
					glVertex3f(coord1.x,coord1.y,coord1.z);
					glVertex3f(coord2.x,coord2.y,coord2.z);
					glVertex3f(coord3.x,coord3.y,coord3.z);
					glEnd();
					Vector norm2 = (coord3-coord4) % (coord1-coord4);
					norm2 = norm2.Normalize();
					glBegin(GL_TRIANGLES);
					glNormal3f(norm2.x,norm2.y,norm2.z);
					glVertex3f(coord3.x, coord3.y, coord3.z);
					glVertex3f(coord4.x, coord4.y, coord4.z);
					glVertex3f(coord1.x, coord1.y, coord1.z);
					glEnd();

			}
		}


		for (int i = 0; i < size; i++) {
			CRSpline c;

			for (int j = 0; j < size; j++) {
				c.ctr.add(cp[i * size + j]);
			}
			c.init();
			c.calcSpeed();
			crc.add(c);
			//c.onDraw();
		}

		for (int i = 0; i < size; i++) {
			CRSpline c;

			for (int j = 0; j < size; j++) {
				c.ctr.add(cp[j * size + i]);
			}
			c.init();
			c.calcSpeed();
			crc.add(c);
			//c.onDraw();
		}
/*
			for (int i = 0; i < crc[0].ctr.getCounter() - 1; i++) {
				for (float u = i; u < i + 1; u += 0.07) {
					CRSpline c1,c2;
					for(int j = 0; j< crc.getCounter()/2;j++){
						c1.ctr.add(crc[j].getCoord(u,i));
						c2.ctr.add(crc[j].getCoord(u+0.07,i));
					}
					c1.init();
					c2.init();
					c1.calcSpeed();
					c2.calcSpeed();

				for (int k = 0; k < c1.ctr.getCounter() - 1; k++) {
					for (float v = k; v < k + 1; v += 0.07) {
						Vector coord1,coord2,coord3,coord4;
						Vector norm1,norm2;
						coord1 = c1.getCoord(v,k);
						coord2 = c2.getCoord(v,k);
						coord3 = c1.getCoord(v+0.07,k);
						coord4 = c2.getCoord(v+0.07,k);
			            float amb[4] = {0.74f, 0.71f, 0.42f, 1.0f};
			            float diff[4] = {0.74f, 0.71f, 0.42f, 1.0f};
			            float spec[4] = {0.5f, 0.5f, 0.5f, 1.0f};

			            glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
			            glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
			            glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
			            glMaterialf(GL_FRONT, GL_SHININESS, 125);
						glBegin(GL_TRIANGLES);
						norm1 = (coord3-coord1) % (coord2-coord1);
						glNormal3f(norm1.x,norm1.y,norm1.z);
						glVertex3f(coord1.x,coord1.y,coord1.z);
						glVertex3f(coord3.x,coord3.y,coord3.z);
						glVertex3f(coord2.x,coord2.y,coord2.z);
						glEnd();
						norm2 = (coord2-coord4) % (coord3-coord4);
						glBegin(GL_TRIANGLES);
						glNormal3f(norm2.x,norm2.y,norm2.z);
						glVertex3f(coord4.x, coord4.y, coord4.z);
						glVertex3f(coord2.x, coord2.y, coord2.z);
						glVertex3f(coord3.x, coord3.y, coord3.z);
						glEnd();
					}
				}


				}
			}
*/
	}
};

class Arrow {
        Vector centerPosition;
        float height;
        float radius;
    public:
        Arrow( ) {
            this->centerPosition = Vector(0.0f, 0.0f, 0.0f);
            this->height = 0.0f;
            this->radius = 0.0f;
        }

        Arrow(Vector newCenterPosition, float newHeight, float newRadius) {
            this->centerPosition = newCenterPosition;
            this->height = newHeight;
            this->radius = newRadius;
        }

        void Draw( ) {

            setMaterialForArrow( );

            Cylinder arrowBody = Cylinder(centerPosition, height, radius);
            arrowBody.Draw( );

            Cone arrowHead = Cone(Vector(centerPosition.x, centerPosition.y + height / 2.0f + 0.25f, centerPosition.z), 1.0f, radius + 0.2f);
            arrowHead.Draw( );
        }
    private:
        void setMaterialForArrow( ) {
            float amb[4] = {0.8f, 0.0f, 0.0f, 1.0f};
            float diff[4] = {0.8f, 0.0f, 0.0f, 1.0f};
            float spec[4] = {0.5f, 0.5f, 0.5f, 1.0f};

            glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
            glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
            glMaterialf(GL_FRONT, GL_SHININESS, 10);
        }
};

class Helicopter {

    Vector centerPosition;

    public:
        Quaternion orientationQuaternion;
        Vector xAxis;
        Vector yAxis;
        Vector zAxis;

        Helicopter( ) {
            this->centerPosition = Vector(0.0f, 0.0f, 0.0f);
            this->xAxis = Vector(1.0f, 0.0f, 0.0f);
            this->yAxis = Vector(0.0f, 1.0f, 0.0f);
            this->zAxis = Vector(0.0f, 0.0f, 1.0f);
            this->orientationQuaternion = Quaternion(1.0f, Vector(0.0f, 0.0f, 0.0f));
        }

        Helicopter(Vector newCenterPosition, Vector newXAxis, Vector newYAxis, Vector newZAxis) {
            this->centerPosition = newCenterPosition;
            this->orientationQuaternion = Quaternion(1.0f, 0.0f, 0.0f, 0.0f);
            this->xAxis = newXAxis;
            this->yAxis = newYAxis;
            this->zAxis = newZAxis;
        }
        void Draw( ) {

            glPushMatrix( );

            Vector normalizedDirection = orientationQuaternion.GetAxis( ).Normalize( );
            Vector midVector = (normalizedDirection + yAxis) / 2.0f;
            float length = fabs(orientationQuaternion.GetRealPart( )) * 15.0f;
            glRotatef(180.0f, midVector.x, midVector.y, midVector.z);
            Arrow arrow = Arrow(Vector(centerPosition.x, centerPosition.y + length / 2.0f, centerPosition.z), length, 0.15f);
            arrow.Draw( );
            glPopMatrix( );

            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

            glPushMatrix( );

            float rotationAngle = orientationQuaternion.GetRotationAngle( );
            Vector axis = orientationQuaternion.GetAxis( );
            glRotatef(rotationAngle, axis.x, axis.y, axis.z);

            Ellipsoid body = Ellipsoid(centerPosition, 1.0f, 1.0f, 2.0f);
            setMaterialForBody( );
            body.Draw('b');
            setMaterialForGlass( );
            glEnable(GL_CULL_FACE);
            body.Draw('g');
            glDisable(GL_CULL_FACE);

            glPushMatrix( );
            glTranslatef(centerPosition.x, centerPosition.y, centerPosition.z - 3.9f);
            glRotatef(90.0f, xAxis.x, xAxis.y, xAxis.z);
            Cylinder c1 = Cylinder(centerPosition, 4.0f, 0.2f);
            setMaterialForBody( );
            c1.Draw( );
            glPopMatrix( );

            glPushMatrix( );
            glTranslatef(centerPosition.x + 0.1f, centerPosition.y, centerPosition.z - 5.7f);
            glRotatef(90.0f, zAxis.x, zAxis.y, zAxis.z);
            Cylinder c2 = Cylinder(centerPosition, 0.4f, 0.2f);
            setMaterialForBody( );
            c2.Draw( );
            glPopMatrix( );

            glPushMatrix( );
            glTranslatef(centerPosition.x, centerPosition.y + 0.9f, centerPosition.z);
            Cylinder c3 = Cylinder(centerPosition, 0.4f, 0.3f);
            setMaterialForBody( );
            c3.Draw( );
            glPopMatrix( );

            glPushMatrix( );
            glTranslatef(centerPosition.x + 2.5f, centerPosition.y + 1.2f, centerPosition.z);
            glRotatef(90.0f, yAxis.x, yAxis.y, yAxis.z);
            Cuboid prop1 = Cuboid(centerPosition, 5.0f, 0.3f, 0.1f);
            setMaterialForPropeller( );
            prop1.Draw( );
            glPopMatrix( );

            glPushMatrix( );
            glTranslatef(centerPosition.x - 2.5f, centerPosition.y + 1.2f, centerPosition.z);
            glRotatef(90.0f, yAxis.x, yAxis.y, yAxis.z);
            Cuboid prop2 = Cuboid(centerPosition, 5.0f, 0.3f, 0.1f);
            setMaterialForPropeller( );
            prop2.Draw( );
            glPopMatrix( );

            glPushMatrix( );
            glTranslatef(centerPosition.x + 0.3f, centerPosition.y, centerPosition.z - 6.2f);
            Cuboid prop3 = Cuboid(centerPosition, 1.0f, 0.1f, 0.1f);
            setMaterialForPropeller( );
            prop3.Draw( );
            glPopMatrix( );

            glPushMatrix( );
            glTranslatef(centerPosition.x + 0.3f, centerPosition.y + 0.4f, centerPosition.z - 5.5f);
            glRotatef(120.0f, xAxis.x, xAxis.y, xAxis.z);
            Cuboid prop4 = Cuboid(centerPosition, 1.0f, 0.1f, 0.1f);
            setMaterialForPropeller( );
            prop4.Draw( );
            glPopMatrix( );

            glPushMatrix( );
            glTranslatef(centerPosition.x + 0.3f, centerPosition.y - 0.4f, centerPosition.z - 5.5f);
            glRotatef(240.0f, xAxis.x, xAxis.y, xAxis.z);
            Cuboid prop5 = Cuboid(centerPosition, 1.0f, 0.1f, 0.1f);
            setMaterialForPropeller( );
            prop5.Draw( );
            glPopMatrix( );

            glPopMatrix( );

            glDisable(GL_BLEND);
        }
    private:

        void setMaterialForBody( ) {
            float amb[4] = {0.74f, 0.71f, 0.42f, 1.0f};
            float diff[4] = {0.74f, 0.71f, 0.42f, 1.0f};
            float spec[4] = {0.5f, 0.5f, 0.5f, 1.0f};

            glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
            glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
            glMaterialf(GL_FRONT, GL_SHININESS, 125);
        }
        void setMaterialForGlass( ) {
            float amb[4] = {0.9f, 0.9f, 0.9f, 0.3f};
            float diff[4] = {0.9f, 0.9f, 0.9f, 0.3f};
            float spec[4] = {0.5f, 0.5f, 0.5f, 0.3f};

            glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
            glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
            glMaterialf(GL_FRONT, GL_SHININESS, 100);
        }

        void setMaterialForPropeller( ){
            float amb[4] = {0.5f, 0.5f, 0.5f, 1.0f};
            float diff[4] = {0.5f, 0.5f, 0.5f, 1.0f};
            float spec[4] = {0.5f, 0.5f, 0.5f, 1.0f};

            glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
            glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
            glMaterialf(GL_FRONT, GL_SHININESS, 25);
        }
};

class Terrain {
    public:
        Terrain( ) {
            this->width = 0.0f;
            this->length = 0.0f;
        };
        Terrain(float newLength, float newWidth) {
            this->width = newWidth;
            this->length = newLength;
        };
        void Draw() {
            createTexture( );
            glBindTexture(GL_TEXTURE_2D, textureID);
            glEnable(GL_TEXTURE_2D);

            float du = 10.0f;
            float dv = 10.0f;
            setMaterialForTerrain( );
            for (float u = 0.0f; u < length; u += du)
                for (float v = 0.0f; v < length; v += dv) {
                    glBegin(GL_TRIANGLE_FAN);
                        glNormal3f(0.0f, 1.0f, 0.0f);
                        glTexCoord2f(0.0f, 0.0f);
                        glVertex3f(v - width / 2.0f, -20.0f, u - length / 2.0f);

                        glTexCoord2f(0.0f, 3.0f);
                        glVertex3f(v - width / 2.0f + dv, -20.f, u - length / 2.0f);

                        glTexCoord2f(3.0f, 3.0f);
                        glVertex3f(v - width / 2.0f + dv, -20.f, u - length / 2.0f + du);

                        glTexCoord2f(3.0f, .0f);
                        glVertex3f(v - width / 2.0f, -20.f, u - length / 2.0f + du);
                    glEnd( );
                }

            glDisable(GL_TEXTURE_2D);

            drawHouses( );
        }
    private:
        float length;
        float width;
        GLuint textureID;

        void createTexture( ) {
            Color texture[128 * 128 * 3];
            Color colors[5] = {Color(0.5f, 0.5f, 0.52f),
            				Color(0.47f, 0.51f, 0.40f),
            				Color(0.51f, 0.53f, 0.52f),
            				Color(0.46f, 0.48f, 0.51f),
            				Color(0.5f, 0.43f, 0.52f)};
            srand(getPseudoRandomSeed( ));
            for (int Y = 0; Y < 128; Y++)
                for (int X = 0; X < 128; X++) {
                	if(Y<5 || Y > 122 || X < 5 || X > 122){
                		texture[Y * 128 + X] = Color(0.2,0.2,0.2);
                	}else{
                		texture[Y * 128 + X] = colors[rand() % 5];
                	}

                }

            glGenTextures(1, &textureID);
            glBindTexture(GL_TEXTURE_2D, textureID);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGB, GL_FLOAT, texture);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        }

        void drawHouses( ) {
            srand(getPseudoRandomSeed());
            for(int i = 0; i < 25; i++) {
                float random = (float)(rand( ) % (int)(width - 10.0f));
                float x = random - ((width - 10.0f) / 2.0f);
                random = (float)(rand( ) % (int)(length - 10.0f));
                float z = random - ((length - 10.0f) / 2.0f);
                Cuboid house = Cuboid(Vector(x, -19.0f, z), 2.0f, 2.0f, 2.0f);
                setMaterialForHouse( );
                house.Draw( );

                Cone roof = Cone(Vector(x, -17.0f, z), 2.0f, 2.1f);
                setMaterialForRoof( );
                roof.Draw( );
            }
        }

        void setMaterialForHouse( ) {
            float amb[4] = {0.3f, 0.3f, 0.3f, 1.0f};
            float diff[4] = {0.3f, 0.3f, 0.0f, 1.0f};
            float spec[4] = {0.5f, 0.5f, 0.5f, 1.0f};

            glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
            glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
            glMaterialf(GL_FRONT, GL_SHININESS, 10);
        }

        void setMaterialForRoof( ) {
			float amb[4] = { 0.74f, 0.71f, 0.42f, 1.0f };
			float diff[4] = { 0.74f, 0.71f, 0.42f, 1.0f };
			float spec[4] = { 0.5f, 0.5f, 0.5f, 1.0f };

			glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
			glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
			glMaterialf(GL_FRONT, GL_SHININESS, 125);
        }

        void setMaterialForTerrain( ) {
            float amb[4] = {0.74f, 0.71f, 0.42f, 1.0f};
            float diff[4] = {0.74f, 0.71f, 0.42f, 1.0f};
            float spec[4] = {0.5f, 0.5f, 0.5f, 1.0f};

            glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
            glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
            glMaterialf(GL_FRONT, GL_SHININESS, 125);
        }

};

Terrain terrain;
Helicopter helicopter;

void onInitialization( ) {

    glEnable(GL_DEPTH_TEST);

    float amb[4] = {0.2f, 0.2f, 0.2f, 0.0f};
    float diff[4] = {0.7f, 0.7f, 0.7f, 0.0f};
    float spec[4] = {1.0f, 1.0f, 1.0f, 0.0f};
    float pos[4] = {100.0f, 100.0f, 100.0f, 0.0};

    glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diff);
    glLightfv(GL_LIGHT0, GL_SPECULAR, spec);
    glLightfv(GL_LIGHT0, GL_POSITION, pos);

    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);

    terrain = Terrain(150.0f, 150.0f);
    helicopter = Helicopter(Vector(0.0f, 0.0f, 0.0f),
                            Vector(1.0f, 0.0f, 0.0f),
                            Vector(0.0f, 1.0f, 0.0f),
                            Vector(0.0f, 0.0f, 1.0f));

}

void onDisplay( ) {
    glClearColor(0.4f, 0.4f, 1.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glViewport(0, 0, screenWidth, screenHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity( );

    gluPerspective(45, 1, 0.5, 100);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity( );

    gluLookAt(-1.0, 3, -15.0,
              5.0, 3.0, 3.0,
              0.0, 1.0, 0.0);
   /* gluLookAt(-15.0, 3, 0.0,
                      5.0, 3.0, 3.0,
                      0.0, 1.0, 0.0);*/
Seal sl,sl2;
sl.init();
sl2.init2();
sl.draw();
sl2.draw();

    terrain.Draw( );


    glutSwapBuffers();
}

void onKeyboard(unsigned char key, int x, int y) {
    if (key == 'R') {
        Quaternion qz = Quaternion(0.0f, helicopter.zAxis);
        Quaternion reciprocal = helicopter.orientationQuaternion.getReciprocal( );
        Vector axis = (helicopter.orientationQuaternion % qz % reciprocal).GetAxis( );
        Quaternion qRoll = Quaternion(cos(20.0f * M_PI / 180.0f / 2.0f), axis * sin(20.0f * M_PI / 180.0f / 2.0f));
        helicopter.orientationQuaternion = qRoll % helicopter.orientationQuaternion;
        glutPostRedisplay( );
    }

    if (key == 'E') {
        Quaternion qz = Quaternion(0.0f, helicopter.zAxis);
        Quaternion reciprocal = helicopter.orientationQuaternion.getReciprocal( );
        Vector axis = (helicopter.orientationQuaternion % qz % reciprocal).GetAxis( );
        Quaternion qRoll = Quaternion(cos(-20.0f * M_PI / 180.0f / 2.0f), axis * sin(-20.0f * M_PI / 180.0f / 2.0f));
        helicopter.orientationQuaternion = qRoll % helicopter.orientationQuaternion;
        glutPostRedisplay( );
    }

    if (key == 'P') {
        Quaternion qx = Quaternion(0.0f, helicopter.xAxis);
        Quaternion reciprocal = helicopter.orientationQuaternion.getReciprocal( );
        Vector axis = (helicopter.orientationQuaternion % qx % reciprocal).GetAxis( );
        Quaternion qPitch = Quaternion(cos(20.0f * M_PI / 180.0f / 2.0f), axis * sin(20.0f * M_PI / 180.0f / 2.0f));
        helicopter.orientationQuaternion = qPitch % helicopter.orientationQuaternion;
        glutPostRedisplay( );
    }

    if (key == 'O') {
        Quaternion qx = Quaternion(0.0f, helicopter.xAxis);
        Quaternion reciprocal = helicopter.orientationQuaternion.getReciprocal( );
        Vector axis = (helicopter.orientationQuaternion % qx % reciprocal).GetAxis( );
        Quaternion qPitch = Quaternion(cos(-20.0f * M_PI / 180.0f / 2.0f), axis * sin(-20.0f * M_PI / 180.0f / 2.0f));
        helicopter.orientationQuaternion = qPitch % helicopter.orientationQuaternion;
        glutPostRedisplay( );
    }

    if (key == 'X') {
        Quaternion qy = Quaternion(0.0f, helicopter.yAxis);
        Quaternion reciprocal = helicopter.orientationQuaternion.getReciprocal( );
        Vector axis = (helicopter.orientationQuaternion % qy % reciprocal).GetAxis( );
        Quaternion qYaw = Quaternion(cos(20.0f * M_PI / 180.0f / 2.0f), axis * sin(20.0f * M_PI / 180.0f / 2.0f));
        helicopter.orientationQuaternion = qYaw % helicopter.orientationQuaternion;
        glutPostRedisplay( );
    }
    if (key == 'Y') {
        Quaternion qy = Quaternion(0.0f, helicopter.yAxis);
        Quaternion reciprocal = helicopter.orientationQuaternion.getReciprocal( );
        Vector axis = (helicopter.orientationQuaternion % qy % reciprocal).GetAxis( );
        Quaternion qYaw = Quaternion(cos(-20.0f * M_PI / 180.0f / 2.0f), axis * sin(-20.0f * M_PI / 180.0f / 2.0f));
        helicopter.orientationQuaternion = qYaw % helicopter.orientationQuaternion;
        glutPostRedisplay( );
    }
}


void onMouse(int button, int state, int x, int y) {
}

void onIdle( ) {
}

// ...Idaig modosithatod
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// A C++ program belepesi pontja, a main fuggvenyt mar nem szabad bantani
int main(int argc, char **argv) {
    glutInit(&argc, argv);                 // GLUT inicializalasa
    glutInitWindowSize(600, 600);            // Alkalmazas ablak kezdeti merete 600x600 pixel
    glutInitWindowPosition(100, 100);            // Az elozo alkalmazas ablakhoz kepest hol tunik fel
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);    // 8 bites R,G,B,A + dupla buffer + melyseg buffer

    glutCreateWindow("Grafika hazi feladat");        // Alkalmazas ablak megszuletik es megjelenik a kepernyon

    glMatrixMode(GL_MODELVIEW);                // A MODELVIEW transzformaciot egysegmatrixra inicializaljuk
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);            // A PROJECTION transzformaciot egysegmatrixra inicializaljuk
    glLoadIdentity();

    onInitialization();                    // Az altalad irt inicializalast lefuttatjuk

    glutDisplayFunc(onDisplay);                // Esemenykezelok regisztralasa
    glutMouseFunc(onMouse);
    glutIdleFunc(onIdle);
    glutKeyboardFunc(onKeyboard);

    glutMainLoop();                    // Esemenykezelo hurok

    return 0;
}
